package com.ld.shieldsb.verifycode;

import java.awt.FontFormatException;
import java.io.IOException;
import java.util.Calendar;
import java.util.Date;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.google.code.kaptcha.Constants;
import com.ld.shieldsb.common.core.model.PropertiesModel;
import com.ld.shieldsb.common.core.model.Result;
import com.ld.shieldsb.common.core.util.ResultUtil;
import com.ld.shieldsb.common.core.util.StringUtils;
import com.ld.shieldsb.common.core.util.date.DateUtil;
import com.ld.shieldsb.verifycode.service.VerifyCodeService;
import com.ld.shieldsb.verifycode.util.ConfigExtend;
import com.ld.shieldsb.verifycode.util.VerifyCodeServiceFactory;

import lombok.extern.slf4j.Slf4j;

/**
 * 验证码工具
 * 
 * @ClassName VerifyCodeUtil
 * @author <a href="mailto:donggongai@126.com" target="_blank">吕凯</a>
 * @date 2019年6月25日 上午8:37:08
 *
 */
@Slf4j
public class VerifyCodeUtil {
    public static final ConfigExtend CONFIG = VerifyCodeService.CONFIG;

    /**
     * 获取实例，注意获取的实例跟配置文件有关
     * 
     * @Title getService
     * @author 吕凯
     * @date 2019年10月23日 下午2:07:19
     * @return VerifyCodeService
     */
    public static VerifyCodeService getService() {
        String authCodeType = getAuthCodeType();
        return VerifyCodeServiceFactory.getInstance().getService(authCodeType);
    }

    public static boolean out(HttpServletRequest request, HttpServletResponse response) throws IOException, FontFormatException {
        return out(request, response, null);
    }

    public static boolean out(HttpServletRequest request, HttpServletResponse response, String sessionKeyCode)
            throws IOException, FontFormatException {
        VerifyCodeService service = getService();
        if (service != null) {
            service.out(request, response, sessionKeyCode);
            return true;
        }
        return false;
    }

    /**
     * 通过配置文件是否需要校验验证码
     * 
     * @Title needCheckVerifyCode
     * @author 吕凯
     * @date 2019年6月25日 上午9:06:34
     * @return boolean
     */
    public static boolean needCheckVerifyCode() {
        return PropertiesModel.CONFIG.getBoolean("checkVerifyCode", true);
    }

    /**
     * 获取验证码类型
     * 
     * @Title getAuthCodeType
     * @author 吕凯
     * @date 2019年10月22日 上午9:16:10
     * @return String
     */
    public static String getAuthCodeType() {
        return CONFIG.getString("authcode.type", VerifyType.GOOGLE.getValue());
    }

    /**
     * 验证码校验，默认字段名称vrifyCode，验证成功后从session中移除，如果是ajax验证不要使用此方法，使用 checkVerifyCode(HttpServletRequest request, String verifyCodeInput,
     * boolean successRemove)
     * 
     * @Title checkVerifyCode
     * @author 吕凯
     * @date 2019年6月25日 上午8:51:53
     * @param request
     * @return Result
     */
    public static Result checkVerifyCode(HttpServletRequest request) {
        return checkVerifyCode(request, request.getParameter("verifyCode"), true);
    }

    /**
     * 验证码校验(校验成功后移除)
     * 
     * @Title checkVerifyCode
     * @author 吕凯
     * @date 2019年6月27日 下午2:31:58
     * @param request
     * @param verifyCodeInput
     *            输入的验证码
     * @return Result
     */
    public static Result checkVerifyCode(HttpServletRequest request, String verifyCodeInput) {
        return checkVerifyCode(request, verifyCodeInput, true);
    }

    /**
     * 
     * 验证码校验(默认5分钟有效)
     * 
     * @Title checkVerifyCode
     * @author 吕凯
     * @date 2019年6月25日 上午8:49:06
     * @param request
     * @param verifyCodeInput
     *            输入的验证码
     * @param successRemove
     *            成功是否移除,ajax验证的时候不需要移除
     * @return Result
     */
    public static Result checkVerifyCode(HttpServletRequest request, String verifyCodeInput, boolean successRemove) {
        return checkVerifyCode(request, null, verifyCodeInput, successRemove, 5);
    }

    /**
     * 
     * 验证码校验
     * 
     * @Title checkVerifyCode
     * @author 吕凯
     * @date 2019年6月27日 下午2:59:58
     * @param request
     * @param verifyCodeInput
     *            输入的验证码
     * @param minutesTimeout
     *            超时时间（分钟）
     * @return Result
     */
    public static Result checkVerifyCode(HttpServletRequest request, String verifyCodeInput, int minutesTimeout) {
        return checkVerifyCode(request, null, verifyCodeInput, true, minutesTimeout);
    }

    /**
     * 验证码校验
     * 
     * @Title checkVerifyCode
     * @author 吕凯
     * @date 2019年6月27日 下午2:25:47
     * @param request
     * @param sessionKeyCode
     *            session中的验证码生成的key
     * @param verifyCodeInput
     *            输入的验证码
     * @param successRemove
     *            成功是否移除,ajax验证的时候不需要移除
     * @param minutesTimeout
     *            超时时间（分钟）
     * @return Result
     */
    public static Result checkVerifyCode(HttpServletRequest request, String sessionKeyCode, String verifyCodeInput, boolean successRemove,
            int minutesTimeout) {
        Result result = new Result();
        String ignoreValue = CONFIG.getIgnoreValue();
        if (StringUtils.isNotEmpty(ignoreValue) && ignoreValue.equals(verifyCodeInput)) { // 忽略的值直接通过
            return ResultUtil.success("验证通过！");
        }
        result.setMessage("请填写正确的验证码！");
        // 获取用户输入的验证码
        if (verifyCodeInput == null) {
            log.warn("输入的验证码为空！");
            result.setMessage("验证码不能为空！");
            result.setSuccess(false);
            return result;
        }
        // 获取生成的验证码
        String sessionKeycodeDate = null; // session中的验证码生成日期的key
        if (StringUtils.isBlank(sessionKeyCode)) {
            sessionKeyCode = CONFIG.getSessionKey();
            sessionKeycodeDate = CONFIG.getSessionDate();
        } else {
            sessionKeycodeDate = sessionKeyCode + "_date";
        }
        String verifyCodeSession = (String) request.getSession().getAttribute(sessionKeyCode);
        if (verifyCodeSession == null) { // 注意此处为空必须返回false，否则存在不触发验证码请求直接登录的情况
            log.warn("验证码" + Constants.KAPTCHA_SESSION_KEY + "不存在！");
            result.setMessage("验证码不存在！");
            result.setSuccess(false);
            return result;
        }
        Date verifyCodeSessionDate = (Date) request.getSession().getAttribute(sessionKeycodeDate); // 需要验证时间，不能一直尝试，一般为5分钟内有效
        if (verifyCodeSessionDate == null) { // 一般不为空，但是以防万一
            verifyCodeSessionDate = DateUtil.addDate(new Date(), -10, Calendar.MINUTE);
        }
        int minutes = DateUtil.minutesBetween(verifyCodeSessionDate, new Date());
        if (minutes >= minutesTimeout) { // 注意此处为空必须返回false，否则存在不触发验证码请求直接登录的情况
            log.warn("验证码" + Constants.KAPTCHA_SESSION_KEY + "超过" + minutesTimeout + "分钟（" + verifyCodeSessionDate + "），请刷新！");
            result.setMessage("验证码超时，请刷新！");
            result.setSuccess(false);
            return result;
        }
        if (verifyCodeInput != null && verifyCodeInput.equalsIgnoreCase(verifyCodeSession)) { // 忽略大小写
            if (successRemove) { // 如果不移除，存在一次校验通过之后，在不重新发起验证码请求的情况下，验证码不变，则可以一直是用该验证码进行校验
                request.getSession().removeAttribute(sessionKeyCode);
            }
            result.setSuccess(true);
        }
        return result;
    }

}